#include <log.hpp>
#include <xmlparse.h>

#include "sc.h"

#include "scu.h"
#include "settings.h"

#include <string.h>

typedef struct
{
  char *name;
  double load, energy;
} report_target_t;

report_target_t *report_target=NULL;
int report_targets=0;
double report_time=0;

CLog clg;
static XML_Parser g_parser = NULL;

void ReadReport(char *name);

extern "C" int MAIN(int argc, char **argv)
{
  scs_target_t *t;
  scs_link_t *l;
  scs_target_operation_t *to;

  double maxload=1;

  int id=-1, lc, dc;
  FILE *f;
  char label[PATH_MAX] = {0};

  if (argc != 3 && argc != 4)
  {
    fprintf(stderr, "Usage: %s <xml specification> [load report] <dot file>\n", argv[0]);
    return -1;
  }

  if (argc == 4)
  {
    lc = 2;
    dc = 3;
  }
  else
  {
    lc = 0;
    dc = 2;
  }
  
  if (!(f = fopen(argv[dc], "w")))
  {
    perror(argv[dc]);
    return -1;
  }

  // Read architecture
  scSetInit();
  scSetRead(argv[1]);
  scSetIndex();
  
  if (lc)
    ReadReport(argv[lc]);
  else
    report_targets = 0;

  fprintf(f, "# SMARTCAM ARCHITECTURE SPECIFICATION. AUTOMATICALLY GENERATED.\n");
  fprintf(f, "digraph G {\n");
  fprintf(f, "  overlap=scale\n");
  //fprintf(f, "  sep=0.25\n");
  fprintf(f, "  epsilon=0.001\n");
  fprintf(f, "  splines=true\n");
  fprintf(f, "  sep=0.1\n");

  sprintf(label,
          "%s\\ntype %d, delay %d, maxtime %4.2f, avgtime %4.2f, avgsqtime %4.2f, avgenergy %4.2f, avgresources %4.2f",
          argv[1], scs_mapper.type, scs_mapper.delay, scs_mapper.maxtime, scs_mapper.avgtime, scs_mapper.avgsqtime, scs_mapper.avgenergy, scs_mapper.avgresources);

  if (lc)
    sprintf(&label[strlen(label)], "\\n%s completed in %4.2f seconds", argv[lc], report_time);

  fprintf(f, "  label=\"%s\"\n", label);

  // Output targets
  while (tblgetnextelement(&scs_targets, &id, &t))
  {
    if (t->address)
    {
      char buf[PATH_MAX]={0}, label[PATH_MAX];
      double Bps = 0;
    
      tblget(&t->operations, TRANSPORT, &to);
      if (to)  Bps = 1.0/to->time;

      strcpy(label, t->name);

      if (t->pos)
	sprintf(&buf[strlen(buf)], ", pos=\"%s!\"", t->pos);

      for (int i=0; i < report_targets; i++)
	if (!strcmp(report_target[i].name, t->name) && report_target[i].load)
	{
	  sprintf(&label[strlen(label)], "\\n%6.2f%% load", report_target[i].load*100);

	  sprintf(&buf[strlen(buf)],
		  ", style=filled fillcolor=\"0 0 %f\" fontcolor=\"0 0 %f\"",
		  1.0-report_target[i].load/maxload, (report_target[i].load/maxload)>0.5?1.0:0.0);
	}

      if (tblgetelements(&t->operations) == 1)
      {
	/* Transport node */
	sprintf(&label[strlen(label)], "\\n%5.3e Bps", Bps);
	sprintf(&buf[strlen(buf)], ", shape=box");
      }
      
      fprintf(f, "  t%d [label=\"%s\"%s]\n", id, label, buf);
    }
  }
  
  // Output connections
  while (tblgetnextelement(&scs_links, &id, &l))
    fprintf(f, "  t%d -> t%d\n", l->source->id, l->destination->id);

  fprintf(f, "}\n");
  
  fclose(f);
  
  return 0;  
}

void startElement(void *userData, const char *name, const char **atts)
{
  if (!strcmp(name, "E"))
  {
    if (!strcmp(atts[0], "load"))
    {
      report_target[report_targets-1].load = atof(atts[1]);
    }
    else if (!strcmp(atts[0], "energy"))
    {
      report_target[report_targets-1].energy = atof(atts[1]);
    }
    else if (!strcmp(atts[0], "time"))
    {
      report_time = atof(atts[1]);
    }
  }
  else
  {
    report_target = (report_target_t*) realloc(report_target,
						(report_targets+1)*sizeof(report_target_t));
    report_target[report_targets].name = (char*) malloc(strlen(name)+1);
    strcpy(report_target[report_targets].name, name);

    report_target[report_targets].load = report_target[report_targets].energy = 0;
    report_targets++;
  }
}

void endElement(void *userData, const char *name)
{
}

void ReadReport(char *name)
{
  FILE *file;
  char buf[BUFSIZ];
  int done;

  // Set up parser
  g_parser = XML_ParserCreate(NULL);
  XML_SetElementHandler(g_parser, startElement, endElement);

  if (!(file = fopen(name, "r")))
  {
    fprintf(stderr, "Couldn't open '%s'\n", name);
    exit(1);
  }

  // Parse
  do {
    size_t len = fread(buf, 1, sizeof(buf), file);
    done = len < sizeof(buf);
    if (!XML_Parse(g_parser, buf, len, done)) {
      fprintf(stderr,
              "%s at line %d\n",
              XML_ErrorString(XML_GetErrorCode(g_parser)),
              XML_GetCurrentLineNumber(g_parser));
      exit(1);
    }
  } while (!done);
  XML_ParserFree(g_parser);

  fclose(file);
}
